/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package game.jetclan.main;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.asset.TextureKey;
import com.jme3.audio.AudioNode;
import com.jme3.audio.AudioRenderer;
import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.FogFilter;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.water.WaterFilter;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.screen.ScreenController;
import de.lessvoid.nifty.tools.SizeValue;
import sun.security.acl.WorldGroupImpl;

/**
 *
 * @author Marcus Wass
 */
public class GameAppState extends JetclanAppState implements ScreenController {

    private FilterPostProcessor fpp;
    private WaterFilter water;
    private Vector3f lightDir = new Vector3f(-4.9f, -1.3f, 5.9f); // same as light source
    private float initialWaterHeight = 0.8f; // choose a value for your scene
    private float time = 0.0f;
    private float waterHeight = 0.0f;
    private Main mainApp;
    private Node rootNode;
    private AssetManager assetManager;
    private AppStateManager stateManager;
    private InputManager inputManager;
    private ViewPort viewPort;
    private AudioRenderer audioRenderer;
    private Node gameTopNode = new Node("Game Top Node");  // some custom class fields...    
    // private final static String NIFTY_XML_NAME = "Interface/ashestoashes_hud.xml";
    private final static String NIFTY_XML_NAME_INIT_SCREEN = "hudScreen";
    private final static String NIFTY_XML_NAME_IN_GAME_MENU = "in_game_menu";
    private final static String NIFTY_XML_ELEMENT_NAME_MINIMAP = "minimapImageId";
    private final static String NIFTY_XML_ELEMENT_NAME_MINIMAPSELECTION = "minimapSelectionPanelId";
    //Geometries and such
    private Texture grassTexture;
//    private static float mSurfaceWidth = 130f;
//    private static float mSurfaceDepth = 130f;
//    private static float mSurfaceThickness = 0.50f;
    FogFilter fogFilter;
    Spatial terrainGeo;
    Spatial skyBox;
    //Sound
    private AudioNode audio_gun;
    private AudioNode audio_nature;
    private RtsCam rtsCam;
    //Map properties
    int mapWidth;
    int mapHeight;
    float minimapRatioX;
    float minimapRatioY;
    Geometry lookAtDebug;
    Geometry camPosDebug;
    Element minimap;
    Element selection;

    public GameAppState(int mapWidthParam, int mapHeightParam) {
        super(StateNameEnum.GAME);
        setEnabled(false);
        mapWidth = mapWidthParam;
        mapHeight = mapHeightParam;
    }

    public Node getGameTopNode() {
        return gameTopNode;
    }  // some custom methods... 

    @Override
    public void initialize(AppStateManager stateManagerParam, Application appParam) {
        super.initialize(stateManager, appParam);
        mainApp = (Main) appParam;
        rootNode = mainApp.getRootNode();
        assetManager = mainApp.getAssetManager();
        stateManager = mainApp.getStateManager();
        inputManager = mainApp.getInputManager();
        audioRenderer = mainApp.getAudioRenderer();
        viewPort = mainApp.getViewPort();

        //TEMP REMOVE ME
        Box a = new Box(1, 1, 1);
        lookAtDebug = new Geometry("LookAt", a);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Red);
        lookAtDebug.setMaterial(mat);
        rootNode.attachChild(lookAtDebug);

        Box b = new Box(1, 1, 1);
        camPosDebug = new Geometry("CamPos", b);

        mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        camPosDebug.setMaterial(mat);
        rootNode.attachChild(camPosDebug);

        // cast to a more specific class
        initInputs();
        initTextures();
        initMap();
        //initManualMap();
        initSkyBox();
        initFog();
        initSound();
        initCamera();
        initMiniMapRatio();
        minimap = nifty.getCurrentScreen().findElementByName(NIFTY_XML_ELEMENT_NAME_MINIMAP);
        selection = nifty.getCurrentScreen().findElementByName(NIFTY_XML_ELEMENT_NAME_MINIMAPSELECTION);
        rootNode.attachChild(gameTopNode);
        setEnabled(true);
    }

    private void initInputs() {

        inputManager.addMapping("ExitGame", new KeyTrigger(KeyInput.KEY_1));
        inputManager.addMapping("ShowInGameMenu", new KeyTrigger(KeyInput.KEY_2));
        //inputManager.addMapping("+SIDE", new KeyTrigger(KeyInput.KEY_D));
        //inputManager.addMapping("+ROTATE", new KeyTrigger(KeyInput.KEY_Q));
        inputManager.addMapping("pick_unit", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        //inputManager.addMapping("unpick_unit", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));

        //inputManager.addListener(analogListener, "unpick_unit");
        //inputManager.addListener(analogListener, "pick_unit");
        inputManager.addListener(gameInputListener, "ExitGame");
        inputManager.addListener(gameInputListener, "ShowInGameMenu");
    }

    @Override
    public void cleanup() {
        super.cleanup();
        // unregister all my listeners, detach all my nodes, etc...
        //rootNode.detachChild(gameTopNode); // modify scene graph...
//        viewPort.removeProcessor(fpp);
        //rootNode.detachChild(terrainGeo);
        rootNode.detachAllChildren();
        stateManager.detach(rtsCam);
        inputManager.removeListener(gameInputListener);
    }

    @Override
    public void setEnabled(boolean enabled) {
        // Pause and unpause
        super.setEnabled(enabled);
        if (enabled) {
            // init stuff that is in use while this state is RUNNING
            //this.app.getRootNode().attachChild(getX()); // modify scene graph...
            //this.app.doSomethingElse();                 // call custom methods...
        } else {
            // take away everything not needed while this state is PAUSED
            //...
        }
    }

    // Note that update is only called while the state is both attached and enabled.
    @Override
    public void update(float tpf) {
        lookAtDebug.setLocalTranslation(rtsCam.center);
        //Get camera center and sync with selection on minimap        
        Vector2f guiCoords = get2DCoordsFrom3DCoords(rtsCam.center, mapWidth, mapWidth);
        moveMiniMapSelectionTo(guiCoords.x, guiCoords.y);

    }
    /**
     * Pick a Target Using the Mouse Pointer. <ol><li>Map "pick target" action
     * to a MouseButtonTrigger. <li>flyCam.setEnabled(false);
     * <li>inputManager.setCursorVisible(true); <li>Implement action in
     * AnalogListener (TODO).</ol>
     */
    private ActionListener gameInputListener = new ActionListener() {
        public void onAction(String name, boolean isPressed, float tpf) {
            //System.out.println("ActionListener: " + name);

            if (name.equals("ExitGame") && isPressed) {
                //System.out.println("ShowGameMenu");
                mainApp.loadMenu();
            } else if (name.equals("ShowInGameMenu") && isPressed) {
                //System.out.println("ShowGameMenu");
                nifty.gotoScreen("in_game_menu");
            }
        }
    };

    //Graphics
    /**
     * Make a solid floor and add it to the scene.
     */
    public void initMap() {

        terrainGeo = assetManager.loadModel("Scenes/myTerrain.j3o");
        rootNode.attachChild(terrainGeo);

    }

    /**
     * Make a solid floor and add it to the scene.
     */
    private void initManualMap() {

        Material terrainMat = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
        terrainMat.setTexture("Alpha", assetManager.loadTexture("Textures/gimp_map/alphamap_plain_gimp.jpg"));

        Texture rock = assetManager.loadTexture("Textures/rock.png");
        rock.setWrap(WrapMode.Repeat);
        terrainMat.setTexture("Tex1", rock);
        terrainMat.setFloat("Tex1Scale", 32);

        Texture grass = assetManager.loadTexture("Textures/grass.png");
        grass.setWrap(WrapMode.Repeat);
        terrainMat.setTexture("Tex2", grass);
        terrainMat.setFloat("Tex2Scale", 64);

        Texture road = assetManager.loadTexture("Textures/road.png");
        road.setWrap(WrapMode.Repeat);
        terrainMat.setTexture("Tex3", road);
        terrainMat.setFloat("Tex3Scale", 64);

        //Heightmap
        Texture heightMapImage = assetManager.loadTexture("Textures/gimp_map/heightmap_plain_gimp.jpg");
        AbstractHeightMap heightMap = null;

        try {
            heightMap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.5f);
            heightMap.load();
        } catch (Exception e) {
            e.printStackTrace();
        }

        TerrainQuad terrainQuad = new TerrainQuad("terrain", 65, 1025, heightMap.getHeightMap());
        terrainQuad.setMaterial(terrainMat);
        rootNode.attachChild(terrainQuad);
    }

    private void initSkyBox() {
        //        Spatial skyBox = assetManager.loadModel("Scenes/skyBoxScene.j3o");
        skyBox = assetManager.loadModel("Scenes/lagoonSkyBox.j3o");
        rootNode.attachChild(skyBox);
    }

    public void initTextures() {

        TextureKey grassTextureKey = new TextureKey("Textures/grass.png");
        grassTextureKey.setGenerateMips(true);
        grassTexture = assetManager.loadTexture(grassTextureKey);
        grassTexture.setWrap(Texture.WrapMode.Repeat);

    }

    private void initFog() {
        /**
         * Add fog to a scene
         */
//        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
//        FogFilter fog = new FogFilter();
//        fog.setFogColor(new ColorRGBA(0.9f, 0.9f, 0.9f, 0.5f));
//        fog.setFogDistance(155);
//        fog.setFogDensity(2.0f);
//        fpp.addFilter(fog);
//        viewPort.addProcessor(fpp);
    }

    private void initSound() {

        /**
         * We create two audio nodes.
         */
        /* gun shot sound is to be triggered by a mouse click. */
        audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false);
        audio_gun.setLooping(false);
        audio_gun.setVolume(2);
        rootNode.attachChild(audio_gun);

        /* nature sound - keeps playing in a loop. */
        audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", false);
        audio_nature.setLooping(true);  // activate continuous playing
        audio_nature.setPositional(false);
        audio_nature.setLocalTranslation(Vector3f.ZERO.clone());
        audio_nature.setVolume(3);
        rootNode.attachChild(audio_nature);
        audio_nature.play(); // play continuously!

    }

    private void initCamera() {
        rtsCam = new RtsCam(RtsCam.UpVector.Y_UP, mainApp.getContext().getSettings().getWidth(), mainApp.getContext().getSettings().getHeight());
        rtsCam.setCenter(new Vector3f(0, 0, 0));
        rtsCam.setDistance(200);
        rtsCam.setMaxSpeed(RtsCam.DoF.FWD, 100, 0.5f);
        rtsCam.setMaxSpeed(RtsCam.DoF.SIDE, 100, 0.5f);
        rtsCam.setMaxSpeed(RtsCam.DoF.DISTANCE, 100, 0.5f);
        stateManager.attach(rtsCam);
    }

    public void bind(Nifty nifty, Screen screen) {
        System.out.println("bind( " + screen.getScreenId() + ")");
    }

    public void onStartScreen() {
        System.out.println("onStartScreen");
    }

    public void onEndScreen() {
        System.out.println("onEndScreen");
    }

    public void returnToGameNiftyEvent() {
        nifty.gotoScreen(NIFTY_XML_NAME_INIT_SCREEN);
    }

    public void miniMapClick(final int mouseX, final int mouseY) {

        //Element minimap = nifty.getCurrentScreen().findElementByName("minimapImageId");

        //Get the mouse position relative to the minimap
        int clickOffsetX = mouseX - (minimap.getX());
        int clickOffsetY = mouseY - (minimap.getY());
        int newCameraXPosition = (int) (clickOffsetX * minimapRatioX);
        int newCameraYPosition = (int) (clickOffsetY * minimapRatioY);

        Vector3f worldCoords = get3DCoordsFrom2DMiniMapCoords(new Vector2f(newCameraXPosition, newCameraYPosition), mapWidth, mapHeight);

        rtsCam.gotoPosition((int) worldCoords.x, (int) worldCoords.z);


    }

    public void released(String object) {
        System.out.println("released object:" + object);
        Element e = nifty.getCurrentScreen().findElementByName("minimapSelectionPanelTEMP");
      //  e.setConstraintX(new SizeValue(String.valueOf(inputManager.getCursorPosition().x)));
       // e.setConstraintY(new SizeValue(String.valueOf(inputManager.getCursorPosition().y)));
        
//        int clickOffsetX = mouseX - (minimap.getX());
//        int clickOffsetY = mouseY - (minimap.getY());
//        int newCameraXPosition = (int) (clickOffsetX * minimapRatioX);
//        int newCameraYPosition = (int) (clickOffsetY * minimapRatioY);
//
//        Vector3f worldCoords = get3DCoordsFrom2DMiniMapCoords(new Vector2f(newCameraXPosition, newCameraYPosition), mapWidth, mapHeight);
//
//        rtsCam.gotoPosition((int) worldCoords.x, (int) worldCoords.z);
        
    }

    private void initMiniMapRatio() {
        Element minimap = nifty.getCurrentScreen().findElementByName("minimapImageId");
        minimapRatioX = mapWidth / minimap.getWidth();
        minimapRatioY = mapHeight / minimap.getHeight();

    }

    /**
     * This method apply on a top down perspective, ie y = 0, and mouse.y click
     * = camera.z axis
     *
     * @param guiCoords
     * @param width
     * @param height
     * @return Coordinate in 3D to place in the world
     */
    static public Vector3f get3DCoordsFrom2DMiniMapCoords(Vector2f guiCoords, int widthWorldMap, int heightWorldMap) {

        float map3DY = 0;
        float map3DX = 0;

        //Change from mouse click position to camera lookat position
        if (guiCoords.x < widthWorldMap / 2) {
            map3DX = (widthWorldMap / 2 - guiCoords.x) * -1;
        } else {
            map3DX = guiCoords.x - widthWorldMap / 2;
        }

        if (guiCoords.y < heightWorldMap / 2) {
            map3DY = (heightWorldMap / 2 - guiCoords.y) * -1;
        } else {
            map3DY = guiCoords.y - heightWorldMap / 2;
        }

        return new Vector3f(map3DX, 0, map3DY);
    }

    /**
     *
     * @param worldCoords
     * @return
     */
    static public Vector2f get2DCoordsFrom3DCoords(Vector3f worldCoords, int widthWorldMap, int heightWorldMap) {
        Vector2f guiCoords;

        float miniMap2DX = 0;
        float miniMap2DY = 0;

        //Change from camera lookat position to mouse click position
        if (worldCoords.x < 0) {
            miniMap2DX = (widthWorldMap / 2 + worldCoords.x);
        } else {
            miniMap2DX = widthWorldMap / 2 + worldCoords.x;
        }

        if (worldCoords.z < 0) {
            miniMap2DY = (heightWorldMap / 2 + worldCoords.z);
        } else {
            miniMap2DY = heightWorldMap / 2 + worldCoords.z;
        }

        return new Vector2f(miniMap2DX, miniMap2DY);
    }

//    private void moveMiniMapSelectionTo(int mouseX, int mouseY) {
//
//        Element selection = nifty.getCurrentScreen().findElementByName("minimapSelectionPanelId");
//        int newSelectionXPosition = mouseX - selection.getWidth() / 2;
//        int newSelectionYPosition = mouseY - selection.getHeight() / 2;
//        selection.setConstraintX(new SizeValue("" + newSelectionXPosition));
//        selection.setConstraintY(new SizeValue("" + newSelectionYPosition));
//        selection.getParent().layoutElements();
//    }
    private void moveMiniMapSelectionTo(float worldCoordinateX, float worldCoordinateY) {

        float minimapCoordX = worldCoordinateX / minimapRatioX;
        float minimapCoordY = worldCoordinateY / minimapRatioY;

        float newSelectionXPosition = minimapCoordX + minimap.getX() - selection.getWidth() / 2;
        float newSelectionYPosition = minimapCoordY + minimap.getY() - selection.getHeight() / 2;
        selection.setConstraintX(new SizeValue("" + (int) newSelectionXPosition));
        selection.setConstraintY(new SizeValue("" + (int) newSelectionYPosition));
        selection.getParent().layoutElements();
    }
}
